import codecs
import re
import math
from loadstone import db

def splitCSV(line):
	result=[]
	s=""
	quoting=False
	for pos,char in enumerate(line):
		if char==",":
			if not quoting:
				if s:
					result.append(s)
				else:
					raise SyntaxError(_("Empty columns not allowed (two consecutive commas in position %d)")%pos)
				s=""
				continue
		elif char == "\"":
			quoting=not quoting
			continue
		s+=char
	if quoting:
		raise SyntaxError(_("Missing quote(s) around label in this line"))
	if s:
		result.append(s)
	return result

class TextParser:
	errors=[]
	lineCount=0
	db=db.DB()

	def loadTable(self,recClass,records):
		state=0 #waiting table header
		header=[]
		for line in self.input:
			line=line.strip()
			self.lineCount+=1
			if line.startswith("#"): continue
			if state==0:
				if not line:
					self.errors.append((self.lineCount,_("syntax error"),_("expected table header but blank line found")))
					continue
				header=line.split(",")
				if set(recClass.fields) != set(header):
					self.errors.append((self.lineCount,_("value error"),_("%s table header is invalid")%recClass.name))
					break
				state=1 #reading data
			elif state ==1: #reading data
				if not line: break #done
				badRecord=False
				try:
					data = splitCSV(line)
				except SyntaxError, e:
					badRecord=True
					self.errors.append((self.lineCount,_("syntax error"),unicode(e)))
				except ValueError,e:
					badRecord=True
					self.errors.append((self.lineCount,_("value error"),unicode(e)))
				if badRecord: continue
				if len(data)<len(recClass.fields):
					self.errors.append((self.lineCount,_("syntax error"),_("Not enough columns")))
					continue
				elif len(data)>len(recClass.fields):
					self.errors.append((self.lineCount,_("syntax error"),_("Too many columns")))
					continue
				r=recClass()
				for i in range(len(header)):
					try:
						r[header[i]]=data[i]
					except SyntaxError, e:
						badRecord=True
						self.errors.append((self.lineCount,_("syntax error"),unicode(e)))
					except ValueError,e:
						badRecord=True
						self.errors.append((self.lineCount,_("value error"),unicode(e)))
				if not badRecord: records.append(r)

	def __init__(self,input):
		"""Parses the input, checks on errors and builds the internal db structure.
		@param input: input stream to read from.
		@type input: IOBase
		"""
		self.input=input
		ignoreFunc = codecs.lookup_error("ignore")
		def ignore_error(err):
			self.errors.append((self.lineCount, _("decode error"), unicode(err)))
			return ignoreFunc(err)
		codecs.register_error("ignore", ignore_error)
		state=0 #waiting table,name
		for line in input:
			line=line.strip()
			self.lineCount+=1
			if state==1: #skipping unknown table
				if not line:
					state=0
					continue
				else:
					continue
			if not line or line.startswith("#"): continue 
			if not line.startswith("table,"):
				self.errors.append((self.lineCount,_("syntax error"),_("Expected starting of table")))
				continue
			else:
				tableName=line.split(",")[1]
			if not re.match(r"\w+",tableName):
				self.errors.append((self.lineCount,_("syntax error"),_("Invalid table name")))
				continue
			if tableName in self.db.tables:
				self.loadTable(self.db.tables[tableName][0],self.db.tables[tableName][1])
			else:
				self.errors.append((self.lineCount,_("syntax error"),_("unknown table \"%s\". Skipping.")%tableName))
				state=1 #skip until blank line
		#cleanup
		codecs.register_error("ignore",ignoreFunc)

def parseTextFile(fileName):
	with codecs.open(fileName,"r","utf-8",errors="ignore") as input:
		return TextParser(input)
